<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="zh" xml:lang="zh" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<head>
<META http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Concept: 桩模块</title>
<meta name="uma.type" content="Concept">
<meta name="uma.name" content="stubs">
<meta name="uma.presentationName" content="桩模块">
<meta name="element_type" content="concept">
<meta name="filetype" content="description">
<meta name="role" content="">
<link rel="StyleSheet" href="./../../../css/default.css" type="text/css">
<script src="./../../../scripts/ContentPageResource.js" type="text/javascript" language="JavaScript"></script><script src="./../../../scripts/ContentPageSection.js" type="text/javascript" language="JavaScript"></script><script src="./../../../scripts/ContentPageSubSection.js" type="text/javascript" language="JavaScript"></script><script src="./../../../scripts/ContentPageToolbar.js" type="text/javascript" language="JavaScript"></script><script src="./../../../scripts/contentPage.js" type="text/javascript" language="JavaScript"></script><script type="text/javascript" language="JavaScript">
					var backPath = './../../../';
					var imgPath = './../../../images/';
					var nodeInfo=null;
					contentPage.preload(imgPath, backPath, nodeInfo,  '', false, false, false);
				</script>
</head>
<body>
<div id="breadcrumbs"></div>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td valign="top"><a name="Top"></a>
<div id="page-guid" value="6.347489919609074E-305"></div>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
<tr>
<td class="pageTitle" nowrap="true">Concept: 桩模块</td><td width="100%">
<div align="right" id="contentPageToolbar"></div>
</td><td width="100%" class="expandCollapseLink" align="right"><a name="mainIndex" href="./../../../index.htm"></a><script language="JavaScript" type="text/javascript" src="./../../../scripts/treebrowser.js"></script></td>
</tr>
</table>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td class="pageTitleSeparator"><img src="./../../../images/shim.gif" alt="" title="" height="1"></td>
</tr>
</table>
<div class="overview">
<table width="97%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="50"><img src="./../../../images/concept.gif" alt="" title=""></td><td>
<table class="overviewTable" border="0" cellspacing="0" cellpadding="0">
<tr>
<td valign="top">该指南描述了如何创建可用作组件（仍需要开发）占位符的桩模块。桩模块是一种组件，它实际上不做任何工作，只需要声明它本身和它接受的参数。桩模块包含刚好足够的代码来允许对它进行编译并与其他组件链接。</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<div class="sectionHeading">Relationships</div>
<div class="sectionContent">
<table class="sectionTable" border="0" cellspacing="0" cellpadding="0">
<tr valign="top">
<th class="sectionTableHeading" scope="row">Related Elements</th><td class="sectionTableCell">
<ul>
<li>
<a href="./../../../rup/workproducts/rup_stub_5B2BA807.html" guid="{6EF8936C-371E-4B2D-B172-0AF461980480}">测试桩模块</a>
</li>
<li>
<a href="./../../../rup/domains/implementation_442FA5B0.html" guid="_M6RHkN7IEdm8G6yT7-Wdqw">实现</a>
</li>
</ul>
</td>
</tr>
</table>
</div>
<div class="sectionHeading">Main Description</div>
<div class="sectionContent">
<table class="sectionTable" border="0" cellspacing="0" cellpadding="0">
<tr valign="top">
<td class="sectionTableSingleCell"><a id="Top" name="Top"></a><a key="存根（stubs）" text="概念" name="XE_stubs__concept_of" id="XE_stubs__concept_of" class="index"></a><a key="测试（test）" text="概念" name="XE_test__stubs__concept_of" id="XE_test__stubs__concept_of" class="index"></a><a key="测试（test）" text="概念" name="XE_test__developer_testing__stubs__concept_of" id="XE_test__developer_testing__stubs__concept_of" class="index"></a> 
<h3>
    <a id="Introduction" name="Introduction">简介</a>
</h3>
<p>
    通过向组件接口发送输入、等待组件处理它们、然后检查结果的方法测试组件。在其处理过程中，某个组件很可能使用其他组件，方法是发送输入给它们并使用它们的结果：
</p>
<p align="center">
    <img height="283" alt="附带文本中描述的图。" src="./../../../rup/guidances/concepts/resources/co_stubs-img1.gif" width="356" />
</p>
<p class="picturetext">
    图 1：测试已实现的组件
</p>
<p>
    这些其他组件可能引起测试问题：
</p>
<ol>
    <li>
        它们可能尚未实现。
    </li>
    <li>
        它们可能有缺陷，阻止您的测试工作或使您花费很多时间来发现测试故障并不是由您的组件所引起的。
    </li>
    <li>
        它们可能使您难以运行所需的测试。如果组件是一个商业数据库，您的公司可能没有为每个人准备足够的浮动许可证。或某一组件可能是只在单独的实验室中、在计划好的时间内才可用的硬件。
    </li>
    <li>
        它们可能使测试变得非常缓慢，以至不能运行足够的测试。例如，初始化数据库可能花费每个测试五分钟。
    </li>
    <li>
        可能难以使组件产生某些结果。例如，您可能希望每个写入磁盘的方法都能处理“磁盘已满”错误。但您如何确保在调用该方法时磁盘已满呢？
    </li>
</ol>
<p>
    为避免这些问题，可以选择使用<b>桩模块</b>组件（也称为<b>模仿对象</b>）。桩模块组件的行为与真实组件类似，至少对响应其测试时组件发送给它们的那些值如此。它们可能做得更好：它们可能是一般用途的<b>仿真器</b>，尽力如实地模仿组件的大多数或全部行为。例如，为硬件构建一个软件仿真器通常是个很好的策略。它们的行为就和硬件一样，只是较慢。它们很有用是因为它们支持更好地调试、它们有更多的副本可用并且可以在硬件完成之前先使用它们。
</p>
<p align="center">
    <img height="283" alt="附带文本中描述的图。" src="./../../../rup/guidances/concepts/resources/co_stubs-img2.gif" width="356" />
</p>
<p class="picturetext">
    图 2：通过完成一个已实现的组件所依赖的桩模块组件来测试已实现的组件
</p>
<p>
    桩模块有两个缺点。
</p>
<ol>
    <li>
        构建它们会很昂贵。（特别是仿真器情况。）因为它们本身是软件，所以也需要维护。
    </li>
    <li>
        它们可能会屏蔽错误。例如，假设您的组件使用三角函数，但还没有库可用。您的三个测试用例求三个角度的正弦值：10 度、45 度和 90
        度。您使用计算器找到正确的值，然后构建一个桩模块分别返回其正弦值：0.173648178、0.707106781 和 1.0。所有都好，直到您将组件与真正的三角函数库集成，该库的正弦函数使用<i>弧度</i>实参，因此返回
        -0.544021111、0.850903525 和 0.893996664。这是您代码中的一个缺陷并将在以后被发现，发现它需要付出更多努力。
    </li>
</ol>
<h3>
    <a id="SoftwareDesignwithStubs" name="SoftwareDesignwithStubs">桩模块与软件设计实践</a>
</h3>
<p>
    除非因为真实组件尚不可用而构建桩模块，否则应在部署后保留它们。它们所支持的测试在产品维护期间可能非常重要。因此编写桩模块需要比一次性代码有更高的标准。虽然它们不需要符合产品代码标准（例如，大多数不需要它们自己的测试套件），但是以后随着产品组件的更改，开发人员将必须维护它们。如果太难以维护，则应丢弃该桩模块并将丢失对它们的所有投入。
</p>
<p>
    特别是当要保留它们时，桩模块改变了组件设计。例如，假设您的组件将使用数据库来持久存储键／值对。考虑两个设计场景：
</p>
<p>
    <b>场景 1：数据库用于测试及常规用法</b>。不需要对组件隐藏数据库的存在。您可能使用数据库名称初始化组件：
</p>
<p>
    <!--webbot bot="HTMLMarkup" startspan -->
</p>
<pre>
public Component(<b>
<font color="#ff0000">
String databaseURL</font></b>) { try {     databaseConnection =         DriverManager.getConnection(databaseURL);     ... } catch (SQLException e) {...}     }
</pre>
<!--webbot bot="HTMLMarkup" endspan -->
<p>
    而且，您虽然不希望在每个读取或写入值的地方构造 SQL 语句，但是肯定会有一些包含 SQL 的方法。例如，需要值的组件代码可能会调用该组件方法：
</p>
<p>
    <!--webbot bot="HTMLMarkup" startspan -->
</p>
<pre>
public String get(String key) { try {     Statement stmt =       databaseConnection.createStatement();     ResultSet rs = stmt.executeQuery(       <b>
<font color="#ff0000">
"SELECT value FROM Table1 WHERE key=" + key</font></b>);     ... } catch (SQLException e) {...}     }
</pre>
<!--webbot bot="HTMLMarkup" endspan -->
<p>
    <b>场景 2：对于测试，使用桩模块替换该数据库。</b>无论是对真实数据库还是桩模块运行组件代码，该代码都应该一致。所以需要将它编写为使用抽象接口方法：
</p>
<p>
    <!--webbot bot="HTMLMarkup" startspan -->
</p>
<pre>
<b>
<font color="#ff0000">
interface KeyValuePairs</font></b> { String <b>
<font color="#ff0000">
get</font></b>(String key); void <b>
<font color="#ff0000">
put</font></b>(String key, String value);     }
</pre>
<!--webbot bot="HTMLMarkup" endspan -->
<p>
    测试时应使用诸如散列表之类的简单内容实现 <b>KeyValuePairs</b>：
</p>
<p>
    <!--webbot bot="HTMLMarkup" startspan -->
</p>
<pre>
<b>
<font color="#ff0000">
class FakeDatabase implements KeyValuePairs</font></b>  { Hashtable table = new Hashtable(); public String <b>
<font color="#ff0000">
get</font></b>(String key) {     return (String) table.get(key); } public void <b>
<font color="#ff0000">
put</font></b>(String key, String value) {     table.put(key, value); }     }
</pre>
<!--webbot bot="HTMLMarkup" endspan -->
<p>
    测试中未使用它时，该组件将使用把对 <font size="+0">KeyValuePairs</font> 接口的调用转换成 SQL 语句的适配器对象：
</p>
<p>
    <!--webbot bot="HTMLMarkup" startspan -->
</p>
<pre>
<b>
<font color="#ff0000">
class DatabaseAdapter implements KeyValuePairs</font></b> { private Connection databaseConnection; public DatabaseAdapter(String databaseURL) {     try {         databaseConnection =             DriverManager.getConnection(databaseURL);         ...     } catch (SQLException e) {...} } public String <b>
<font color="#ff0000">
get</font></b>(String key) {     try {         Statement stmt =            databaseConnection.createStatement();         ResultSet rs = stmt.executeQuery(           "SELECT value FROM Table1 WHERE key=" + key);         ...     } catch (SQLException e) {...} } public void <b>
<font color="#ff0000">
put</font></b>(String key, String value) {     ... }     }
</pre>
<!--webbot bot="HTMLMarkup" endspan -->
<p>
    您的组件可能有用于测试和其他客户端的单个构造函数。该构造函数将采用实现 <font size="+0">KeyValuePairs</font> 的对象。或它可能提供仅用于测试的接口，需要组件的一般客户端传入数据库名称：
</p>
<p>
    <!--webbot bot="HTMLMarkup" startspan -->
</p>
<pre>
class Component { <b>
<font color="#ff0000">
public</font></b> Component(String databaseURL) {     this.valueStash = new DatabaseAdapter(databaseURL); } // For testing. <b>
<font color="#ff0000">
protected</font></b> Component(KeyValuePairs valueStash) {     this.valueStash = valueStash; }     }
</pre>
<!--webbot bot="HTMLMarkup" endspan -->
<p>
    这样，从客户端程序员的视角，两个设计场景产生相同的 API，但一个更易于测试。（注意某些测试可能使用真实数据库，而另一些可能使用桩模块数据库。）
</p>
<h3>
    <a id="FurtherInformation" name="FurtherInformation">进一步的信息</a>
</h3>
<p>
    与桩模块有关的更多信息，请参阅以下内容：
</p>
<ul>
    <li>
        <a href="http://www.connextra.com/aboutUs/mockobjects.pdf" target="_blank"><i>Endo-Testing: Unit testing with Mock
        Objects</i></a>, "eXtreme Programming and Flexible Processes in Software Engineering - XP2000". &copy; 2000 Tim
        Mackinnon, Steve Freeman, Philip Craig.
    </li>
    <li style="LIST-STYLE-TYPE: none">
        <br />
        （<a href="http://www.adobe.com/products/acrobat/alternate.html" target="_blank">获得 Adobe Reader</a>）
    </li>
    <li>
        <a class="elementLinkWithType" href="./../../../rup/tools/rup_quality_architect_3444CDC5.html" guid="{11A91795-5221-4C65-A9DE-EE431CEA6993}">Tool: Rational QualityArchitect</a>
    </li>
    <li>
        <a class="elementLinkWithType" href="./../../../rup/tools/rup_test_realtime_861C5353.html" guid="{18E6764A-2366-4F59-A5F2-FDF4891EBCE9}">Tool: Rational Test RealTime</a><br />
        <br />
    </li>
</ul><br /></td>
</tr>
</table>
</div>
<table class="copyright" border="0" cellspacing="0" cellpadding="0">
<tr>
<td class="copyright">Copyright &copy; 2008 版权所有 东软集团股份有限公司&nbsp; 联系邮箱:<a href="mailto:tcoe@neusoft.com">tcoe@neusoft.com</a></td>
</tr>
</table>
</td>
</tr>
</table>
</body>
<script type="text/javascript" language="JavaScript">
				contentPage.onload();
			</script>
</html>
